#include "load.h"
#include <string.h>
#include <stdio.h>

#define MAX_LODE_SYMBLE_NUM	0xFF


#define EXT_SYMBLE_FUNC_CALL_START(func)	\
	__attribute__((used)) __attribute__((section("EXT_SYMBLE.0"))) const struct sym_type_t ext_sym_##func = {	\
		.magic = SYMBLE_MAGIC,		\
		.name = #func,				\
		.type = SYMBLE_TYPE_FUNC,	\
		.addr = NULL,				\
	};

#define EXT_SYMBLE_FUNC_CALL_END(func)	\
	__attribute__((used)) __attribute__((section("EXT_SYMBLE.end"))) const struct sym_type_t ext_sym_##func = {	\
		.magic = SYMBLE_MAGIC,		\
		.name = #func,				\
		.type = SYMBLE_TYPE_FUNC,	\
		.addr = NULL,				\
	};
	
typedef int (*entry_func_t)(void);

EXT_SYMBLE_FUNC_CALL_START(start)
EXT_SYMBLE_FUNC_CALL_END(end)

const struct sym_type_t* get_ext_symble(char *name)
{
	int size = (&ext_sym_end - &ext_sym_start);
	const struct sym_type_t* target;
	for (int i = 1; i < size; i ++) {
		target = &(&ext_sym_start)[i];
		if (strcmp(target->name, name) == 0) {
			return target;
		}
	}
	return NULL;
}
	
int load_module(struct module_t* handle, uint32_t addr)
{
	entry_func_t entry;
	
	handle->head = (const struct rom_head_t*)addr;
	
	if (handle->head->magic != ROM_HEAD_MAGIC) {
		return -LOAD_ERR_NOT_FIND_HEAD;
	}
	
	handle->ext = (const struct sym_type_t*)handle->head->ext_sym_addr;
	handle->ref = (struct sym_type_t*)handle->head->ref_sym_addr;
	handle->ext_sym_size = 0;
	handle->ref_sym_size = 0;
	
	for (int i = 0; i < MAX_LODE_SYMBLE_NUM; i ++) {
		if (handle->ext[i].magic != SYMBLE_MAGIC) {
			break;
		}
		if (strcmp(handle->ext[i].name, "ext_func_end") == 0) {
			break;
		}
		handle->ext_sym_size += 1;
	}
	
	entry = (entry_func_t)handle->head->entry_addr;
	entry();
	
	for (int i = 0; i < MAX_LODE_SYMBLE_NUM; i ++) {
		if (handle->ref[i].magic != SYMBLE_MAGIC) {
			break;
		}
		if (strcmp(handle->ref[i].name, "ref_func_end") == 0) {
			break;
		}
		if (strcmp(handle->ref[i].name, "ref_func_start") == 0) {
			continue;
		}
		const struct sym_type_t* sym_src = get_ext_symble(handle->ref[i].name);
		if (sym_src == NULL) {
			return -LOAD_ERR_NOT_FIND_EXT_SYM;
		}
		
		memcpy(&handle->ref[i], sym_src, sizeof(struct sym_type_t));
		
		handle->ref_sym_size += 1;
	}
	
	return 0;
}

